﻿/* Copyright (C) Siemens AG, 2012. All Rights Reserved.
 * Author: Roland Eckl, Corporate Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.ServiceModel;
using RVM.Infrastructure.Identification;


namespace RVM.Infrastructure.Communication.WCF.NetPipe
{
    /// <summary>
    /// Exporting using WCF and net.pipe binding.
    /// TODO: This exporter is always using the EventWaitHandle for signalling, which should be placed into the ProcessIdentificationProvider only!
    /// </summary>
    public class NetPipeViewModelExporter : IDisposable, IViewModelExporter
    {
        #region Private Members

        /// <summary>
        /// Unique ID of the exporter. Taken from the ProcessIdentificationProvider.
        /// </summary>
        private string _exporterId = string.Empty;

        /// <summary>
        /// IdentificationProvider, used to create new Id and to signal/monitor other identities.
        /// </summary>
        private IIdentificationProvider _idProvider = null;

        /// <summary>
        /// Corresponding session object, which is shared among compatible importers and exporters.
        /// </summary>
        private Session _session = null;

        /// <summary>
        /// ServiceHost for the net.pipe based IPC.
        /// </summary>
        private ExtendedServiceHost _host = null;

        /// <summary>
        /// Associated ViewModelRegistry.
        /// </summary>
        private ViewModelRegistry _registry;

        /// <summary>
        /// Flag if server pipe is currently open.
        /// </summary>
        private bool _connected;

        /// <summary>
        /// List of importing agents.
        /// </summary>
        private List<IViewModelAgent> _importers = new List<IViewModelAgent>();

        #endregion

        #region Public Members

        /// <summary>
        /// Event, raised whenever an importer is registered or removed.
        /// </summary>
        public event Action<int> ImporterCountChanged;

        /// <summary>
        /// Gives the count of registered importers.
        /// </summary>
        public int ImporterCount { get { return _importers.Count; } }

        /// <summary>
        /// Public Accessor for the exporter's unique Id.
        /// </summary>
        public string Id { get { return _exporterId; } }

        /// <summary>
        /// Public Accessor if the export is active.
        /// </summary>
        public bool Connected { get { return _connected; } }

        /// <summary>
        /// Public Accessor of the session's shared Id.
        /// </summary>
        public string SessionId
        { 
            get
            {
                if (_session != null)
                    return _session.Id;
                return string.Empty;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructors, selects the local process ID as unique ID.
        /// </summary>
        /// <param name="exporterId">Id of Exporter</param>
        /// <param name="sharedSession">Session identifying compatible exporters and importers</param>
        public NetPipeViewModelExporter(IIdentificationProvider idProvider, Session sharedSession) : this(idProvider, sharedSession, ViewModelRegistry.Shared) { }

        /// <summary>
        /// Default constructors, selects the local process ID as unique ID.
        /// </summary>
        /// <param name="idProvider">Id of Exporter</param>
        /// <param name="sharedSession">Session identifying compatible exporters and importers</param>
        /// <param name="registry">Registry to export</param>
        public NetPipeViewModelExporter(IIdentificationProvider idProvider, Session sharedSession, ViewModelRegistry registry)
        {
            _idProvider = idProvider;
            _exporterId = idProvider.CreateNewIdentity();
            _session = sharedSession;
            _registry = registry;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Establishes an net.pipe server object, which allows importers to connect.
        /// </summary>
        /// <returns>TRUE, if successfull</returns>
        public bool Start()
        {
            string pipe = "net.pipe://localhost/" + SessionId + "/" + _exporterId;
            const string endpoint = @"/ViewModelExporter";
           
            try
            {
                NetNamedPipeBinding binding = new NetNamedPipeBinding();
                _host = new ExtendedServiceHost(typeof (NetPipeViewModelAgent),
                                        new Uri[] { new Uri(pipe) }, _registry, this);
                binding.ReceiveTimeout = TimeSpan.MaxValue;
                _host.AddServiceEndpoint(typeof(IViewModelAgent),
                                            binding,
                                            endpoint);
                _host.Open();
                _connected = true;
            }
            catch (Exception)
            {
                _connected = false;
                return false;
            }

            // Signal (using EventWaitHandle with Exporter ID - this one should be unique but equal under all exporters/importers which belong together).
            _idProvider.Signal(true);

            return _connected;
        }

        /// <summary>
        /// Closes the net.pipe server object, importers are automatically disconnected.
        /// </summary>
        /// <returns>TRUE, if successfull</returns>
        public bool Stop()
        {
            // Remove all agents from associated registry.
            foreach(IViewModelAgent importer in _importers)
            {
                _registry.RemoveAgentFromAll(importer);
            }
            _importers.Clear();

            try
            {
                _host.Close(new TimeSpan(0,0,0,0,200));
                _host = null;
                _connected = false;
            }
            catch (Exception)
            {
                _connected = false;
                return false;
            }
            _idProvider.Signal(false);

            return !_connected;
        }

        #endregion

        #region Internal Methods

        /// <summary>
        /// Adds an importer. Must only be called by NetPipeViewModelAgent!
        /// </summary>
        /// <param name="importer"></param>
        internal void AddImporter(IViewModelAgent importer)
        {
            _importers.Add(importer);
            if (ImporterCountChanged != null)
                ImporterCountChanged(_importers.Count);
        }

        /// <summary>
        /// Removes an importer. Must only be called by NetPipeViewModelAgent!
        /// </summary>
        /// <param name="importer"></param>
        internal void RemoveImporter(IViewModelAgent importer)
        {
            if (_importers.Contains(importer))
            {
                _importers.Remove(importer); 
            }
            if (ImporterCountChanged != null)
                ImporterCountChanged(_importers.Count);
        }

        #endregion

        #region IDisposable

        /// <summary>
        /// Clean up everything!
        /// </summary>
        public void Dispose()
        {
            if (_connected)
                Stop();
        }

        #endregion
    }
}
